import CoordTransformUtils.CoordTransformService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.uber.h3core.H3CoreLoader;
import com.uber.h3core.util.GeoCoord;
import lombok.extern.slf4j.Slf4j;
import org.geotools.data.*;
import org.geotools.data.postgis.PostgisNGDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.GeometryBuilder;
import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import com.uber.h3core.H3Core;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.index.kdtree.KdNode;
import org.locationtech.jts.index.kdtree.KdTree;
import org.locationtech.jts.index.quadtree.Quadtree;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.opengis.feature.Feature;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.GeometryType;
import org.opengis.filter.*;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import utilityTools.GeoJsonUtiles;
import utilityTools.SPIUtils;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

//将度换算成米，公式为：degree = meter / (2 * Math.PI * 6371004) * 360
//因为GeoTools中在计算缓冲区时，使用的是度，所以需要根据实际来进行转换

/**
 * 进行GeoTools的一些基本接口的使用测试
 */
@Slf4j
public class GeotoolsOperation {

    @Resource(name = "h3Engine")
    private H3Core h3Engine;

    private final String crsSource = "EPSG:4326";
    private final String crsTarget = "EPSG:3857";

    public void testUnion() throws Exception {
        String pathGeojson = "E:\\9.1GISData\\33333333.geojson";
        File fileSource = new File(pathGeojson);
        JSONObject jsonObjectSource = GeoJsonUtiles.getGeoJsonSource(fileSource);
        JSONArray featureArray = jsonObjectSource.getJSONArray("features");
        GeometryJSON geojsonFactory = new GeometryJSON();
        Reader readerStr = null;
        JSONObject featureInput = featureArray.getJSONObject(0);
        readerStr = new StringReader(featureInput.toJSONString());
        Geometry geoInput = geojsonFactory.read(readerStr);//虽然是整个feature的字符串，但也可以直接读取到几何结果
        JSONObject featureOther = featureArray.getJSONObject(1);
        readerStr = new StringReader(featureOther.toJSONString());
        Geometry geoOther = geojsonFactory.read(readerStr);
        Geometry geoOutput = geoInput.union(geoOther);
        if (geoOutput.isEmpty()) {
            System.out.println("union结果几何是空");
        }
        System.out.println("几何结果集：" + geoOutput.toText());
        String rrrr = "E:\\9.1GISData\\demounion.geojson";
        File filerrrrr = new File(rrrr);
        OutputStream osrrrr = new FileOutputStream(filerrrrr);
        geojsonFactory.write(geoOutput, osrrrr);
        osrrrr.flush();
        osrrrr.close();
    }

    public void testGeometryWkt() throws Exception {
        String wktPolygon_Other = "POLYGON((77.457034547465611 43.949983701892023, " +
                "88.086932895925074 44.128138422815923, 88.086932895925074 44.128138422815923, " +
                "96.163280244475274 40.208734562490093, 93.550344337591383 36.051791074265722, " +
                "91.353102779529934 30.172685283776975, 82.207827105436323 25.837587074628704, " +
                "75.437947710328075 32.666851376711591, 77.457034547465611 43.949983701892023))";
        String wktPolygon_Input = "POLYGON((117.110858 40.70836,117.286401 40.660719,"
                + "117.389879 40.228141,117.389879 40.5617,117.110858 40.70836))";
        GeometryFactory geoFactory = JTSFactoryFinder.getGeometryFactory();
        WKTReader wktReader = new WKTReader(geoFactory);
        Geometry geometryNew = wktReader.read(wktPolygon_Input);
//        GeometryJSON gjFactory = new GeometryJSON();
//        String strGeometry=gjFactory.toString(geometryNew);
//        System.out.println("原始Geometry的Json字符串："+strGeometry);
//        JSONObject jjGeo=JSONObject.parseObject(strGeometry);
//        System.out.println("包装后获取的coordinates："+jjGeo.getString("coordinates"));
//        System.out.println(geometryNew.getNumPoints());
////        System.out.println(geometryNew.getCoordinates().toString());//这是是类型，而不是值
//        for (Coordinate coordinate : geometryNew.getCoordinates()) {
//            System.out.println(coordinate.toString());
//
//        }
        String tmp = GeoJsonUtiles.convertGeometry2Wkt(geometryNew);
        System.out.println(tmp);
    }

    public void testReadWkt() throws Exception {
//        String wktPolygon = "POLYGON((117.110858 40.70836,117.286401 40.660719," +
//                "117.389879 40.228141,117.389879 40.5617,117.110858 40.70836))";
//        String wktPoint = "POINT(117.23852 34.78695)";
//        String wktCollection = "GEOMETRYCOLLECTION(POINT(116.23852 33.78695),POINT(117.23852 34.78695),POINT(118.23852 35.78695))";
//        WKTReader reader = new WKTReader();
//        Geometry geometryNew = reader.read(wktPoint);
//        System.out.println("几何类型：" + geometryNew.getGeometryType());
//        Geometry geo2 = reader.read(wktCollection);
//        if (geo2 instanceof GeometryCollection) {
//            System.out.println("Collection读取后的类型：" + geo2.getGeometryType());
//        } else {
//            System.out.println("不能使用instanceof判断");
//        }
        int cnts = 0;
        Iterator<DataStoreFactorySpi> availableDS = DataStoreFinder.getAvailableDataStores();
        while (availableDS.hasNext()) {
            cnts++;
            System.out.println("***---***");
            DataStoreFactorySpi perSpi = availableDS.next();
            System.out.println("DisplayName:" + perSpi.getDisplayName());
            DataAccessFactory.Param[] params = perSpi.getParametersInfo();
            for (DataAccessFactory.Param someParm : params) {
//                System.out.println(someParm.getValue());
                System.out.println(someParm.getDefaultValue());
            }
        }
        System.out.println("执行完毕,供个" + cnts + "参数。");
    }

    public void test求两点距离() throws Exception {
        String pathShp = "E:\\9.1GISData\\spatialautocorrelation\\pointSpatialAutoCorrelation2.shp";
        SimpleFeatureSource fs = (SimpleFeatureSource) ShpfileReader.getShpfile2(pathShp, "utf-8");
        SimpleFeatureIterator itSource = fs.getFeatures().features();
        List<Geometry> geolist = new ArrayList<>();
        while (itSource.hasNext()) {
            SimpleFeature sf = itSource.next();
//            geolist.add(((Geometry) sf.getGeometry()));
            geolist.add(((Geometry) sf.getDefaultGeometry()));
        }
        System.out.println("距离计算结果输出：");
        //直接按度来计算
//        System.out.println(geolist.get(0).getCoordinates()[0].getX()+";"+geolist.get(0).getCoordinates()[0].getY());
//        System.out.println(geolist.get(1).getCoordinates()[0].getX()+";"+geolist.get(1).getCoordinates()[0].getY());
//        double disDegree=geolist.get(0).distance(geolist.get(1));
//        System.out.println(disDegree);//计算的结果是弧度值
//        double disMeter=disDegree/((2*Math.PI*6371004)*360);//这样的结果不对，必须先投影再计算
//        System.out.println(disMeter);
        //坐标参考内容获取
        CoordinateReferenceSystem crsSource = fs.getSchema().getCoordinateReferenceSystem();
        System.out.println(crsSource.getName().getCode());//GCS_WGS_1984
        System.out.println(crsSource.getCoordinateSystem().getName().getCode());//GCS_WGS_1984
        System.out.println(crsSource.getName().getCodeSpace());//null
        System.out.println(CRS.toSRS(crsSource));
        CoordinateReferenceSystem crsNew = CRS.decode(crsSource.getName().getCode());
        System.out.println(crsNew.getName());
        //转换后再计算
        double[] xyGeo1 = new double[]{geolist.get(0).getCoordinates()[0].getX(), geolist.get(0).getCoordinates()[0].getY()};
        double[] xyGeo2 = new double[]{geolist.get(1).getCoordinates()[0].getX(), geolist.get(1).getCoordinates()[0].getY()};
        double[] xyTransed1 = CoordTransformService.crsTransform("EPSG:4326", "EPSG:3857", xyGeo1[0], xyGeo1[1], true);
        double[] xyTransed2 = CoordTransformService.crsTransform("EPSG:4326", "EPSG:3857", xyGeo2[0], xyGeo2[1], true);
        double disCalced = Math.sqrt(Math.pow(xyTransed1[0] - xyTransed2[0], 2) + Math.pow(xyTransed1[1] - xyTransed2[1], 2));
        System.out.println("手工计算结果：" + disCalced);
        Geometry geoTrans1 = CoordTransformService.crsTransform("EPSG:4326", "EPSG:3857", geolist.get(0));
        Geometry geoTrans2 = CoordTransformService.crsTransform("EPSG:4326", "EPSG:3857", geolist.get(1));
        double disTransed = geoTrans1.distance(geoTrans2);
        System.out.println("GeoTools计算结果：" + disTransed);
    }

    public void test空间权重矩阵() throws Exception {
        String pathShp = "E:\\9.1GISData\\spatialautocorrelation\\pointSpatialAutoCorrelation2.shp";
        SimpleFeatureSource fs = (SimpleFeatureSource) ShpfileReader.getShpfile2(pathShp, "utf-8");
        SimpleFeatureIterator itSource = fs.getFeatures().features();
        List<Geometry> geolist = new ArrayList<>();
        while (itSource.hasNext()) {
            SimpleFeature sf = itSource.next();
//            geolist.add(((Geometry) sf.getGeometry()));
            geolist.add(((Geometry) sf.getDefaultGeometry()));
        }
        Geometry[] geometries = geolist.stream().toArray(Geometry[]::new);
        double[][] swmSource = buildSWM(geometries);
        double[] minValuesPerRow = getThresholdDistance(swmSource);
        Arrays.sort(minValuesPerRow);
        for (double v : minValuesPerRow) {
            System.out.println(v);
        }
        System.out.println("默认阈值是：" + minValuesPerRow[minValuesPerRow.length - 1]);
//        //原始空间权重矩阵
//        Double[][] swmSource = buildSpatialWightMatrixByInverseDistance(geometries);
//        //设置最小临近点的个数
//        int nearPnts=14;
//        Double[] result=getmydata(swmSource,nearPnts);
//        Arrays.sort(result);//对结果进行升序排列
//        System.out.println("最小临近点为{"+nearPnts+"}个的结果");
//        for (int i = result.length-1; i >result.length-10; i--) {
//            System.out.println(result[i]);
//        }
//        System.out.print(System.lineSeparator());//换行符的打印
//        Double ttt=0.0d;
//        for (Double perMin : result) {
//            ttt+=perMin;
//        }
//        System.out.println("平均值是："+ttt / result.length);
    }

    private Double[][] buildSpatialWightMatrixByInverseDistance(Geometry[] geometriesSource) {
        Envelope clipEnvelpoe = new Envelope();
        int lengthOfMatrix = geometriesSource.length;
//        double disThreshold = 51473.9184;//空间权重矩阵计算时的距离阈值，大于它的都赋值为0
        Double[][] spatialWightMatrix = new Double[lengthOfMatrix][lengthOfMatrix];
        //‘从左至右，从上至下’的顺序去填充空间权重矩阵
        for (int i = 0; i < lengthOfMatrix; i++) {
            Geometry iGeo = CoordTransformService.crsTransform(crsSource, crsTarget, geometriesSource[i]);
            clipEnvelpoe.expandToInclude(iGeo.getCoordinate());
            for (int j = i; j < lengthOfMatrix; j++) {
                if (j == i) {
                    spatialWightMatrix[i][j] = 999999.999999d;//矩阵的斜线上值为0
                } else {
                    Geometry jGeo = CoordTransformService.crsTransform(crsSource, crsTarget, geometriesSource[j]);
                    double ij = iGeo.distance(jGeo);
                    spatialWightMatrix[i][j] = ij;
                    spatialWightMatrix[j][i] = ij;
                }
            }
        }
        System.out.println("坐标转换后的Extent-max值：" + clipEnvelpoe.maxExtent());
        System.out.println("坐标转换后的Extent-min值：" + clipEnvelpoe.minExtent());
        return spatialWightMatrix;
    }

    private Double[] getmydata(Double[][] matrixInput, int nearNums) {
        Double[][] midddleMatrix = matrixInput.clone();
        Double[] rslt = new Double[matrixInput.length];//创建一个行数长度的数组
        for (int rowIndex = 0; rowIndex < midddleMatrix.length; rowIndex++) {
            Arrays.sort(midddleMatrix[rowIndex]);//对该行进行升序排列
            //打印前nearNums个要素
            for (int clnIndex = 0; clnIndex < nearNums; clnIndex++) {
                System.out.print(midddleMatrix[rowIndex][clnIndex] + "  ");
            }
            System.out.print(System.lineSeparator());
            //获取最小临近点个数的距离
            rslt[rowIndex] = midddleMatrix[rowIndex][nearNums - 1];
        }
        return rslt;
    }

    private double[] getThresholdDistance(double[][] swmSource) {
        int lenghtMatrix = swmSource.length;
        double[] minValuesOfPerRow = new double[lenghtMatrix];
        for (int i = 0; i < lenghtMatrix; i++) {
            Arrays.sort(swmSource[i]);
            for (int j = 0; j < 3; j++) {
                System.out.println(swmSource[i][j]);
            }
            minValuesOfPerRow[i] = swmSource[i][1];//排序后，一般第二个元素是最小值
        }
        return minValuesOfPerRow;
//        Arrays.sort(minValuesOfPerRow);
//        return minValuesOfPerRow[lenghtMatrix - 1];//返回所有最小值中的最大值
    }

    private double[][] buildSWM(Geometry[] geometriesSource) {
        int lengthOfMatrix = geometriesSource.length;
//        double disThreshold = 51473.9184;//空间权重矩阵计算时的距离阈值，大于它的都赋值为0
        double[][] swm = new double[lengthOfMatrix][lengthOfMatrix];
        //‘从左至右，从上至下’的顺序去填充空间权重矩阵
        for (int i = 0; i < lengthOfMatrix; i++) {
            Geometry iGeo = CoordTransformService.crsTransform(crsSource, crsTarget, geometriesSource[i]);
            for (int j = i + 1; j < lengthOfMatrix; j++) {
                Geometry jGeo = CoordTransformService.crsTransform(crsSource, crsTarget, geometriesSource[j]);
                //求欧氏距离
                double ij = iGeo.distance(jGeo);
                swm[i][j] = ij;
                swm[j][i] = ij;
            }
        }
        return swm;
    }

    /*
     * 计算两个Geometry的距离
     * 1.直接使用Geometry的distance方法，在‘面’类型下，计算的是两个面上最近点的距离；
     * 2.Geometry的getCentroid方法获取的是质心；而getInterior方法获取的点不一定在面内部（有可能在外部）；
     *   因此在使用中，可以使用getCentroid去获取多边形的中心（因为此时是无密度的）
     * */
    public void calcDistance() throws Exception {
        //获取原始图形数据
        String pathShp_Polygon = "E:\\9.1GISData\\polygon2single.shp";
        SimpleFeatureSource sfs = (SimpleFeatureSource) ShpfileReader.getShpfile2(pathShp_Polygon, "utf-8");
        SimpleFeatureIterator itSource = sfs.getFeatures().features();
        System.out.println(sfs.getSchema().getGeometryDescriptor().getType().getName().toString());
        List<Geometry> geolist = new ArrayList<>();
        int cntTest = 0;
        while (itSource.hasNext()) {
            SimpleFeature feature = itSource.next();
            System.out.println(feature.getDefaultGeometryProperty().getType().getBinding().getTypeName());
            System.out.println(feature.getDefaultGeometryProperty().getType().getName());
            System.out.println(feature.getType().getTypeName());//这个是获取的shp的名字
            if (cntTest < 3) {
                String geoName = feature.getAttribute("name").toString();
                System.out.println(geoName);
                geolist.add(((Geometry) feature.getDefaultGeometry()));
            } else {
                break;
            }
            cntTest++;
        }
        //开始计算测试
        for (int idx = 0; idx < geolist.size(); idx++) {
            Geometry iGeo = geolist.get(idx);
            Point iPnt = iGeo.getCentroid();
            System.out.println("质心坐标[原始]：" + iPnt.getCoordinate().getX() + "," + iPnt.getCoordinate().getY());//质心坐标打印出来是没有问题的
            double[] iCoords = CoordTransformService.crsTransform(crsSource, crsTarget, iPnt.getX(), iPnt.getY(), false);
            System.out.println("A使用xy方式转换：" + iCoords[0] + ";" + iCoords[1]);
            Geometry iPntConverted = CoordTransformService.crsTransform(crsSource, crsTarget, iPnt);
            System.out.println("使用几何方式转换：" + iPntConverted.getCoordinate().getX() + "," + iPntConverted.getCoordinate().getY());
            System.out.println("====================");
            Geometry jGeo = geolist.get(idx + 1);
            Point jPnt = jGeo.getCentroid();
            System.out.println("质心坐标[原始]：" + jPnt.getX() + "," + jPnt.getY());
            double[] jCoords = CoordTransformService.crsTransform(crsSource, crsTarget, jPnt.getX(), jPnt.getY(), false);
            System.out.println("B使用xy方式转换：" + jCoords[0] + ";" + jCoords[1]);
            double dis = iGeo.distance(jGeo);//源码：计算的是两个几何体上最近点的距离
            System.out.println("直接用面几何计算的距离：" + dis);
            double disCalc = Math.sqrt(Math.pow(iCoords[0] - jCoords[0], 2) + Math.pow(iCoords[1] - jCoords[1], 2));
            System.out.println("使用坐标计算的距离是：" + disCalc);
            break;
        }
    }

    public void test几何类型判断() throws Exception {
        String pathShp = "E:\\9.1GISData\\spatialautocorrelation\\pointSpatialAutoCorrelation.shp";
        SimpleFeatureSource sfs = (SimpleFeatureSource) ShpfileReader.getShpfile2(pathShp, "utf-8");
        GeometryType typeGeo = sfs.getSchema().getGeometryDescriptor().getType();
        System.out.println(typeGeo.getClass());//org.geotools.feature.type.GeometryTypeImpl
        System.out.println("原始类型字符串：" + typeGeo.getBinding());
        if (typeGeo.getBinding().equals(Geometry.class)) {
            System.out.println("可以等于Geometry");
        }
        if (Geometry.class.isAssignableFrom(typeGeo.getBinding())) {
            System.out.println("可以执行判断");
        }
        if (typeGeo.getBinding().equals(Point.class)) {
            System.out.println("是Point");
        } else if (typeGeo.getBinding().equals(Polygon.class) || typeGeo.getClass().equals(MultiPolygon.class)) {
            System.out.println("是Polygon");
        } else {
            System.out.println("不支持的几何类型");
        }
    }

    public void getDemoGeometry() throws Exception {
        String pathGeojson = "E:\\9.1GISData\\geojson-polygontwoSingle.geojson";
        String pathGeojson_Pnt = "E:\\9.1GISData\\geojson_point.json";
        File fileGeojson = new File(pathGeojson);
        JSONObject source = GeoJsonUtiles.getGeoJsonSource(fileGeojson);
        JSONArray feaArray = source.getJSONArray("features");
        JSONObject joProperties = feaArray.getJSONObject(0).getJSONObject("properties");
        System.out.println(joProperties.getString("name"));
        JSONObject joGeo = feaArray.getJSONObject(0).getJSONObject("geometry");
        System.out.println("直接获取geojson的Geometry的字符串：" + joGeo.toJSONString());
        String wktGeo = GeoJsonUtiles.convertGeometry2Wkt(joGeo);
        System.out.println("转换为wkt之后的字符串：" + wktGeo);
        //===============================
        String wktGeoCollection = "geometrycollection(point(116.405285 39.904989),point(117.190182 39.125596)," +
                "point(114.502461 38.045474),point(112.549248 37.857014))";
        WKTReader wktReader = new WKTReader();
        Geometry geoFromWKT = wktReader.read(wktGeo);
        double[] boundaryCoords = getBoundaryCoordsSelf(geoFromWKT.getCoordinates());
        System.out.println("外接矩形 ：" + geoFromWKT.getEnvelope().toText());//获取的不是最小外接矩形，而是由最大、最小坐标组成的一个矩形
        System.out.println("面积1：" + geoFromWKT.getEnvelope().getArea());
        Envelope envelopeMid = geoFromWKT.getEnvelopeInternal();
        System.out.println("外接矩形2：" + JTS.toGeometry(envelopeMid).toText());
        System.out.println("面积2：" + envelopeMid.getArea());
        //==============================
        GeometryJSON gjFactory = new GeometryJSON();
        System.out.println(gjFactory.toString(geoFromWKT.getEnvelope()));
        System.out.println("执行完毕");
    }

    private double[] getBoundaryCoordsSelf(Coordinate[] coordinates) {
        List<Double> xValues = new ArrayList<Double>();
        List<Double> yValues = new ArrayList<Double>();
        for (Coordinate perCoord : coordinates) {
            xValues.add(perCoord.getX());
            yValues.add(perCoord.getY());
        }
        xValues.sort(new Comparator<Double>() {
            @Override
            public int compare(Double o1, Double o2) {
                return o1 > o2 ? 1 : -1;
            }
        });//这种形式时：进行升序排列
        System.out.println("X_Max:" + xValues.get(0));
        System.out.println("X_Min:" + xValues.get(xValues.size() - 1));
        yValues.sort(new Comparator<Double>() {
            @Override
            public int compare(Double o1, Double o2) {
                return o1 > o2 ? 1 : -1;
            }
        });
        System.out.println("Y_Max:" + yValues.get(0));
        System.out.println("Y_Min:" + yValues.get(yValues.size() - 1));
        return new double[]{xValues.get(0), yValues.get(0), xValues.get(xValues.size() - 1), yValues.get(yValues.size() - 1)};
    }

    public void testEPSG坐标系() throws Exception {
        CoordinateReferenceSystem crs4490 = CRS.decode("EPSG:4490", true);
        System.out.println("4490:" + crs4490.toWKT());
        System.out.println(crs4490.getName().getCode());
        System.out.println("00000===00000");
        String prj4490 = CRS.getProjectedCRS(crs4490) == null ? "无投影" : CRS.getProjectedCRS(crs4490).toWKT();
        System.out.println("4490投影" + prj4490);
        CoordinateReferenceSystem crs4479 = CRS.decode("EPSG:4479", true);
        System.out.println("4479:" + crs4479.toWKT());
        String prj4479 = CRS.getProjectedCRS(crs4479) == null ? "无投影" : CRS.getProjectedCRS(crs4479).toWKT();
        System.out.println("4479投影：" + prj4479);
        CoordinateReferenceSystem crs4480 = CRS.decode("EPSG:4480", true);
        System.out.println(crs4480.getName().getCode());
//        System.out.println("4480:"+crs4480.toWKT());//这个是无法打印出来的。
    }

    FilterFactory2 filterFactory = CommonFactoryFinder.getFilterFactory2();

    public void test普通空间查询() {
        try {
            //打开原始点集数据
            String filePath = "E:\\9.1GISData\\idwTest\\countcenterofshanxi.shp";
            SimpleFeatureSource sfs = ShpfileReader.getShpfile2(filePath, "GBK");
            String geoAttrName = sfs.getSchema().getGeometryDescriptor().getLocalName();
            String tblName = sfs.getSchema().getTypeName();
            System.out.println("获取的TypeName是：" + tblName);
            long timeStart = System.currentTimeMillis();
            //创建搜索多边形
            double rDegree = 0.3;
            Polygon calcCircle = createCircle(109.0624, 34.0579, rDegree);
            Filter geoFilter = filterFactory.intersects(filterFactory.property(geoAttrName), filterFactory.literal(calcCircle));
            //创建Query对象
            Query queryByGeo = new Query(tblName);
            queryByGeo.setFilter(geoFilter);
            SimpleFeatureCollection sfc = sfs.getFeatures(queryByGeo);
            System.out.println("查询结果是：" + sfc.size());
            long timeEnd = System.currentTimeMillis();
            sfs.getDataStore().dispose();//这一句就是shp文件的释放方式
            System.out.println("[普通查询]耗时:" + (timeEnd - timeStart) / 1000);
        } catch (Exception error) {
            System.out.print("执行失败：");
            System.out.println(error.getMessage());
        }
    }

    public void test四叉树空间索引() {
        try {
            //打开原始点集数据
            String filePath = "E:\\9.1GISData\\idwTest\\countcenterofshanxi.shp";
            SimpleFeatureCollection sfc = ShpfileReader.getShpfile2(filePath, "GBK").getFeatures();
            SimpleFeatureIterator iterator = sfc.features();
            System.out.println("shp的坐标系是：" + System.lineSeparator() + sfc.getSchema().getCoordinateReferenceSystem().toWKT());
            //建立四叉树索引
            int flag = 0;
            Quadtree quadtree = new Quadtree();
            Date start = new Date();
            while (iterator.hasNext()) {
                SimpleFeature ft = iterator.next();
                Point pntSource = (Point) ft.getDefaultGeometry();
                quadtree.insert(pntSource.getEnvelopeInternal(), ft);
                if (flag == 0) {
                    System.out.println("点的Envelope的面积：" + pntSource.getEnvelopeInternal().getArea());
                    flag++;
                }
            }
            System.out.println("[建立四叉树索引]耗时:" + (new Date().getTime() - start.getTime()) / 1000);
            //建立模拟搜索框
            start = new Date();
            //以下4行是什么含义，没有理解
//            Envelope search = new Envelope(pntSearch.getCoordinate());
//            OctagonalEnvelope searchGeom = new OctagonalEnvelope(pntSearch.getCoordinate());
//            searchGeom.expandBy(0.001);
//            search.expandBy(0.001);
            double rDegree = 0.001;//30000/1000*KILOMETRE_VS_DEGREE;

            //建立一个半径30000m的圆作为搜索区域
            GeometryBuilder geoBuilder = new GeometryBuilder();
            Polygon polygonSearch = geoBuilder.circle(109.0624, 34.5079, rDegree, 32);
            Polygon calcCircle = createCircle(109.0624, 34.0579, rDegree);
            System.out.println("圆的面积是：" + calcCircle.getArea());
            System.out.println("圆的Envelope的面积是：" + calcCircle.getEnvelopeInternal().getArea());
            //这种建立搜索范围的方式不可行
//            Envelope ttt=new Envelope();
//            ttt.expandBy(20000);
            //执行搜索
            List result = quadtree.query(calcCircle.getEnvelopeInternal());
            for (Object ob : result) {
                SimpleFeature sft = (SimpleFeature) ob;
                System.out.println(sft.getAttribute("NAME").toString());
            }
            System.out.println("查询结果数量：" + result.size());

            System.out.println("总数据量:" + quadtree.size() + "条");
            System.out.println("四叉树深度：" + quadtree.depth());
            System.out.println("四叉树树索引查找耗时:" + (new Date().getTime() - start.getTime()));
        } catch (Exception error) {
            System.out.println("执行出错：" + error.getMessage());
        }
    }

    public void testKdTree索引() {
        try {
            //打开原始点集数据
            String filePath = "E:\\9.1GISData\\idwTest\\countcenterofshanxi.shp";
            SimpleFeatureCollection sfc = ShpfileReader.getShpfile2(filePath, "GBK").getFeatures();
            SimpleFeatureIterator iterator = sfc.features();
            KdTree kdTree = new KdTree();
            //建立索引
            while (iterator.hasNext()) {
                SimpleFeature ft = iterator.next();
                Point pntSource = (Point) ft.getDefaultGeometry();
                kdTree.insert(pntSource.getCoordinate());
            }
            //执行查询
            double rDegree = 0.2;
            Polygon calcCircle = createCircle(109.0624, 34.0579, rDegree);
            List rsltQuery = kdTree.query(calcCircle.getEnvelopeInternal());
            System.out.println(rsltQuery.get(0).getClass().toString());
            KdNode node = ((KdNode) rsltQuery.get(0));
            Object nobj = node.getData();
            if (nobj == null) {
                //kdtree的getData是否有值，在于执行insert时使用了那个方法
                //kdtree的node只能获取到坐标信息
                System.out.println("getData结果为空");
            }
            System.out.println(node.getCoordinate().toString());
//            for (Object o : rsltQuery) {
//                System.out.println(o.getClass().toString());
//            }
            System.out.println("查询结果数量为：" + rsltQuery.size());
        } catch (Exception error) {
            System.out.println("执行失败");
            System.out.println(error.getMessage());
        }
    }

    private GeometryFactory geometryFactory = new GeometryFactory();

    private Polygon createCircle(double x, double y, final double RADIUS) {
        final int SIDES = 32; // 确定边数
        Coordinate coords[] = new Coordinate[SIDES + 1];
        for (int i = 0; i < SIDES; i++) {
            double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;
            double dx = Math.cos(angle) * RADIUS;
            double dy = Math.sin(angle) * RADIUS;
            coords[i] = new Coordinate((double) x + dx, (double) y + dy);
        }
        coords[SIDES] = coords[0];
        LinearRing ring = geometryFactory.createLinearRing(coords); // 画一个环线
        Polygon polygon = geometryFactory.createPolygon(ring, null); // 生成一个面
        return polygon;
    }

    public void test判断String是wkt或geojson() {
        Geometry geoResult;
        try {
            String strWKT = "POLYGON((117.110858 40.70836,117.286401 40.660719,117.389879 40.228141," +
                    "117.389879 40.5617,117.110858 40.70836))";
            String strGeojson = "{ \"type\": \"MultiPolygon\", " +
                    "\"coordinates\": [ [ [ [ 115.183823242330959, 36.029383221281918 ], [ 115.204938777480578, 36.027892712918415 ], " +
                    "[ 115.204938777480578, 36.027892712918415 ], [ 115.212515528328382, 36.015596018919524 ], " +
                    "[ 115.20667770390466, 36.000815144314792 ], [ 115.184941123603593, 35.992865766376113 ], " +
                    "[ 115.168918158695945, 36.008267686132299 ], [ 115.183823242330959, 36.029383221281918 ] ] ] ] }";
            WKTReader wktReader = new WKTReader();
            geoResult = wktReader.read(strGeojson);
            if (geoResult != null) {
                System.out.println("面积是：" + geoResult.getArea());
            } else if (JSONObject.isValid(strGeojson)) {
                GeometryJSON geoJSON = new GeometryJSON();
                geoResult = geoJSON.read(new StringReader(strGeojson));
                System.out.println("面积是：" + geoResult.getArea());
            } else {
                throw new IllegalArgumentException("The input value is illegal, Please checkout and enter a valid WKT or GeoJSON.");
                //输入值既不是合法的WKT字符串，也不是合法的GeoJSON字符串，请检查后重新输入！
            }
        } catch (Exception error) {
            System.out.println("转换出错");
            error.printStackTrace();
        }
    }

    public void test获取PostGIS数据() throws IOException {
        //PostGIS连接参数也可以使用全部string的方式
        //Map<String, Object> params = new HashMap<String, Object>();
        //params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");
        //params.put(PostgisNGDataStoreFactory.HOST.key, "192.168.108.50");
        //params.put(PostgisNGDataStoreFactory.PORT.key, "5432");
        //params.put(PostgisNGDataStoreFactory.DATABASE.key, "gisdb");
        //params.put(PostgisNGDataStoreFactory.SCHEMA.key, "public");
        //params.put(PostgisNGDataStoreFactory.USER.key, "postgres");
        //params.put(PostgisNGDataStoreFactory.PASSWD.key, "gisAdmin.1");

        Map<String, Object> params = new HashMap<>();
        params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");
        params.put(PostgisNGDataStoreFactory.HOST.key, "10.136.106.89");
        params.put(PostgisNGDataStoreFactory.PORT.key, 5432);
        params.put(PostgisNGDataStoreFactory.DATABASE.key, "gisdb");
        params.put(PostgisNGDataStoreFactory.USER.key, "postgres");
        params.put(PostgisNGDataStoreFactory.SCHEMA.key, "public");
        params.put(PostgisNGDataStoreFactory.PASSWD.key, "gisAdmin.1");
        params.put(PostgisNGDataStoreFactory.MAXCONN.key, "2500");
        params.put(PostgisNGDataStoreFactory.MAXWAIT.key, 60);
        DataStore dsSource = DataStoreFinder.getDataStore(params);
        //针对PostGIS类型的数据，不能像shp那样，直接使用getTypeNames()[0]的方式，因为获取的是整个库中的所有空间表(还包含其他部分表)
        //SimpleFeatureType mytable = dsSource.getSchema("mytable");//如果表名称不存在会直接报错的，所以要进行处理
        boolean hasMytable = Arrays.stream(dsSource.getTypeNames()).anyMatch(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.equalsIgnoreCase("mytable");
            }
        });
        if (hasMytable) {
            log.warn("存在该表");
        } else {
            log.warn("没有找到mytable表。");
        }
        //int cnts=0;
        //String[] typeNamesSource=dsSource.getTypeNames();
        //for (String perTypeName : typeNamesSource) {
        //    System.out.println(perTypeName);
        //    cnts++;
        //    if (cnts>5) {
        //        break;
        //    }
        //}
        String tableName = "CitiesOfSX";
        SimpleFeatureType schema = dsSource.getSchema(tableName);
        try {
            System.out.println("bindingName名称" + schema.getClass().getName());
            schema.getUserData().put("total.count", 5);
        } catch (Exception error) {
            log.error("修改userData错误：", error);
        }
        log.error("===添加日志并执行成功===");

    }

    //geojson导入PG
    public boolean importGeojson2PG(String geojsonpath, String tablename) throws IOException {
        //还需要验证一下路径的有效性
        DataStore pgDatastore = null;//创建PG的DataStore对象//postgisDataStore.getInstance();
        FeatureJSON featureJSON = new FeatureJSON();
        FeatureCollection featureCollection = featureJSON.readFeatureCollection(new FileInputStream(geojsonpath));
        SimpleFeatureType geojsontype = (SimpleFeatureType) featureCollection.getSchema();
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.init(geojsontype);
        typeBuilder.setName(tablename);
        SimpleFeatureType newtype = typeBuilder.buildFeatureType();
        pgDatastore.createSchema(newtype);

        FeatureIterator iterator = featureCollection.features();
        FeatureWriter<SimpleFeatureType, SimpleFeature> featureWriter = pgDatastore.getFeatureWriterAppend(tablename, Transaction.AUTO_COMMIT);

        while (iterator.hasNext()) {
            Feature feature = iterator.next();
            SimpleFeature simpleFeature = featureWriter.next();
            Collection<Property> properties = feature.getProperties();
            Iterator<Property> propertyIterator = properties.iterator();
            while (propertyIterator.hasNext()) {
                Property property = propertyIterator.next();
                simpleFeature.setAttribute(property.getName().toString(), property.getValue());
            }
            featureWriter.write();
        }
        iterator.close();
        featureWriter.close();
        pgDatastore.dispose();
        return true;
    }

    //PG导出为geojson
    /*
     * 这里应该支持属性的选择，即PG导出时，要能够勾选哪些字段导出
     * */
    public boolean exportPG2Geojson(String tablename, String geojsonPath) throws IOException, FactoryException {
        DataStore pgDatastore = null;//创建PG对象//postgisDataStore.getInstance();
        FeatureSource featureSource = pgDatastore.getFeatureSource(tablename);
        FeatureCollection featureCollection = featureSource.getFeatures();
        FeatureJSON featureJSON = new FeatureJSON();
        File file = new File(geojsonPath);
        if (!file.exists()) {
            file.createNewFile();
        }
        OutputStream outputStream = new FileOutputStream(file, false);
        featureJSON.writeFeatureCollection(featureCollection, outputStream);
        pgDatastore.dispose();
        return true;
    }

    public void testOpenGeojson() throws IOException, FactoryException {
        String fileGeojson = "E:\\gislogs\\demoProvince.geojson";
        String fileGeojsonNonCRS = "E:\\gislogs\\demoProvinceNonCRS.geojson";
        FeatureJSON featureJSON = new FeatureJSON();
        FeatureCollection featureCollection = featureJSON.readFeatureCollection(new FileInputStream(fileGeojson));
        ReferencedEnvelope bounds = featureCollection.getBounds();
        System.out.println(bounds.copy().toString());
        System.out.println(bounds.toString());
        System.out.println("-*-*-*-Bounds: " + featureCollection.getBounds().toString());//有无坐标系都可以输出
        //当没有crs参数时，tippecanoe仍然可以切片（不论有无crs，生成的metadata中都没有crs信息）
        FeatureType schema = featureCollection.getSchema();
        CoordinateReferenceSystem crs = schema.getCoordinateReferenceSystem();
        if (crs == null) {
            System.out.println("无坐标参考信息");
        }
        System.out.println(schema.getDescriptor("name").toString());
        File ff = new File(fileGeojsonNonCRS);
        String name = ff.getName().substring(0, ff.getName().lastIndexOf("."));
        System.out.println("other: " + name);
        System.out.println("getName: " + ff.getName());
        System.out.println("url.getFile: " + ff.toURI().toURL().getFile());
        System.out.println("=============另一种打开方式=============");
    }

    /*
    测试结果：
    （1）GeoJson
        properties属性中不能含有带子对象的字段，否则会报错；
        而如果没有的话，可以正常获取type和bound
    （2）shp
        跟属性字段的值没有关系；
     */
    public void testGetFileGeojson() throws IOException, FactoryException {
        String fileGeojsonNoChildObj = "C:\\Users\\BB\\Desktop\\JLJ\\demo.geojson";
        String fileShp = "C:\\Users\\BB\\Desktop\\JLJ\\转测\\data\\newXian_shp.shp";
        String shpNameNonExtent = "newXian_shp";
        //构造DataStore
        File file = new File(fileShp);
        Map<String, Serializable> map = new HashMap<>();
        map.put("url", file.toURI().toURL());
        DataStore dataStore = DataStoreFinder.getDataStore(map);
        //获取数据
//        ((ShapefileDataStore) dataStore).setCharset(Charset.forName("utf-8"));
        String typeName = dataStore.getTypeNames()[0];
        //可以看出使用shp时DataStore获取tablename的两种方式
        log.warn("getTypeNames()[0]: {}", typeName);
        log.warn("name from FilePath: {}", shpNameNonExtent);
        SimpleFeatureType shpSchema = dataStore.getSchema(shpNameNonExtent);
        log.warn("Schema.getTypeName(): {}", shpSchema.getTypeName());
        SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);//24.1在这里并没有初始化schema
        if (featureSource == null) {
            System.out.println("不能获取featuresource");
            return;
        }
//        System.out.println("数量："+featureSource.getCount(null));
//        SimpleFeatureType type = featureSource.getSchema();
//        ReferencedEnvelope bounds = featureSource.getBounds();
//        System.out.println("方式1-count："+featureSource.getFeatures().size());//这种方式可以正常获取数量
//        SimpleFeatureCollection featureCollection = featureSource.getFeatures();
//        ReferencedEnvelope bounds = featureCollection.getBounds();
//        SimpleFeatureType type = featureCollection.getSchema();
//        SimpleFeatureIterator featureIterator = featureCollection.features();
//        while (featureIterator.hasNext()) {
//            SimpleFeature perFeature = featureIterator.next();
//            System.out.println(perFeature.getID());
//        }
//        //System.out.println("要素数量："+featureSource.getCount(null));//切换后，shp文件在这里也会报错
//        System.out.println("maxX:"+bounds.getMaxX());
//        CoordinateReferenceSystem crs2 = type.getCoordinateReferenceSystem();
//        if (crs2==null) {
//            System.out.println("仍然无法获取坐标系");
//        }else {
//            Integer code=CRS.lookupEpsgCode(crs2, true);
//            if (code==null) {
//                System.out.println("没有对应的EPSG编码");
//                code=-1;
//            }
//            System.out.println("EPSG："+code);
//        }
    }

    public void test获取属性类型() throws Exception {
        String pathShp = "E:\\9.1GISData\\spatialautocorrelation\\pointSpatialAutoCorrelation.shp";
        SimpleFeatureSource sfs = (SimpleFeatureSource) ShpfileReader.getShpfile2(pathShp, "utf-8");
        Integer epsgCode = CRS.lookupEpsgCode(sfs.getSchema().getCoordinateReferenceSystem(), true);
        System.out.println("获取的EPSG的编码是：" + epsgCode);
        SimpleFeatureType sfType = sfs.getSchema();
        System.out.println("---For Geometry---");
        GeometryType geometryType = sfType.getGeometryDescriptor().getType();
        CoordinateReferenceSystem crs = sfType.getCoordinateReferenceSystem();
        System.out.println(geometryType.getBinding().getTypeName());
        System.out.println(geometryType.getName().getLocalPart());
        System.out.println(crs.getName().getCode());
        System.out.println(CRS.lookupEpsgCode(crs, true));//仅输出4326，并不包括‘EPSG:’部分
//        GeometryType typeGeo = sfs.getSchema().getGeometryDescriptor().getType();
//        System.out.println(typeGeo.getClass());//org.geotools.feature.type.GeometryTypeImpl
//        System.out.println("原始类型字符串：" + typeGeo.getBinding());
//        if (typeGeo.getBinding().equals(Geometry.class)) {
//            System.out.println("可以等于Geometry");
//        }
//        if (Geometry.class.isAssignableFrom(typeGeo.getBinding())) {
//            System.out.println("可以执行判断");
//        }
//        if (typeGeo.getBinding().equals(Point.class)) {
//            System.out.println("是Point");
//        } else if (typeGeo.getBinding().equals(Polygon.class) || typeGeo.getClass().equals(MultiPolygon.class)) {
//            System.out.println("是Polygon");
//        } else {
//            System.out.println("不支持的几何类型");
//        }
    }

    public void testCollection操作() throws Exception {
        String pathShp = "E:\\9.1GISData\\spatialautocorrelation\\pointSpatialAutoCorrelation.shp";
        File file = new File(pathShp);
        Map<String, Object> map = new HashMap<>();
        map.put("url", file.toURI().toURL());
        DataStore shpDataStore = DataStoreFinder.getDataStore(map);
        String typeName = shpDataStore.getTypeNames()[0];
        SimpleFeatureSource shpFeaSource = shpDataStore.getFeatureSource(typeName);
        SimpleFeatureCollection collection = shpFeaSource.getFeatures();
        if (!collection.isEmpty()) {

        }
        //这里可以这么执行，不会出现执行了isEmpty之后获取不到几何列的情况
        //但是使用GeoMesa连接HDFS获取collection之后，如果执行isEmpty的话，后续就获取不到几何列，不知道原因；
        SimpleFeature[] fArray = new SimpleFeature[collection.size()];
        collection.toArray(fArray);
        List<SimpleFeature> list = new ArrayList<>(Arrays.asList(fArray));
        for (SimpleFeature feature : list) {
            for (Property property : feature.getProperties()) {
                System.out.println(property.getName().toString());
            }
            break;
        }
        shpDataStore.dispose();
    }

    public void crsTransform() {
        String srcCrs = "EPSG:4545";
        String dstCrs = "EPSG:4326";
        //示例：POINT(2000_X 2000_Y)
        String geometrySource = "POINT(630015.7854 4055192.743)";
        try {
            WKTReader reader = new WKTReader();
            Geometry geometry = reader.read(geometrySource);
            //初始化CRS时要设置参数，保证经度在前纬度在后
            MathTransform transform = CRS.findMathTransform(CRS.decode(srcCrs, true),
                    CRS.decode(dstCrs, true), true);
            Geometry geometryResult = JTS.transform(geometry, transform);
            String result = geometryResult.toText();
            System.out.println("转换结果：" + result);
            //转换结果(15位小数)：POINT (109.45339145390118 36.61881209380758)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void crsTransform2() {
        String srcCrs = "EPSG:4326";
        String dstCrs = "EPSG:4326";
        //示例：POINT(2000_X 2000_Y)
        String geometrySource = "POINT(117.782379 34.145226)";
        try {
            WKTReader reader = new WKTReader();
            Geometry geometry = reader.read(geometrySource);
            //初始化CRS时要设置参数，保证经度在前纬度在后
            MathTransform transform = CRS.findMathTransform(CRS.decode(srcCrs),
                    CRS.decode(dstCrs, true));
            Geometry geometryResult = JTS.transform(geometry, transform);
            String result = geometryResult.toText();
            System.out.println("转换结果：" + result);
            //转换结果(15位小数)：POINT (109.45339145390118 36.61881209380758)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    String wkt = "POLYGON((112.85056435 34.10493819 ,113.19804368 33.90432091,113.19804368 33.50308634,112.85056435 33.30246906," +
            "112.50308502 33.50308634,112.50308502 33.90432091,112.85056435 34.10493819))";

    public void test格网分析() throws IOException, TransformException, ParseException {
        //luoyang_counties
        DataStore pgDS = connPostGIS.connPg();
        double resolution = 0.401234567;
        SimpleFeatureSource sfsSource = pgDS.getFeatureSource("luoyang_counties");
        //System.out.println(sfsSource.getSchema().getGeometryDescriptor().getLocalName());//可以得到几何列的列名
        CoordinateReferenceSystem crsSource = sfsSource.getSchema().getCoordinateReferenceSystem();
        ReferencedEnvelope envelopeSource = sfsSource.getBounds();
        System.out.println("原始的bounds：" + envelopeSource.toBounds(crsSource));
        WKTReader wktReader = new WKTReader();
        Geometry geometryGrid = wktReader.read(wkt);
        SimpleFeatureIterator iterator = sfsSource.getFeatures().features();
        while (iterator.hasNext()) {
            SimpleFeature feature = iterator.next();
            Geometry geometrySrc = (Geometry) feature.getDefaultGeometry();
            if (geometrySrc.intersects(geometryGrid)) {
                System.out.println("产生交集的对象是： " + feature.getAttribute("NAME"));
                break;
            }
        }
        System.out.println("没有任何交集");
        //ReferencedEnvelope envelopeGrid = Envelopes.expandToInclude(envelopeSource, resolution*2);
        //System.out.println("Grid的bounds："+envelopeGrid.toBounds(crsSource));
        //GridProcess gridProcess=new GridProcess();
        //SimpleFeatureCollection sfcGrid = gridProcess.execute(envelopeGrid, resolution, null, null, GridProcess.GridMode.HexagonFlat);
        //System.out.println("生成的原始grid数量："+sfcGrid.size());
        //System.out.println("最后结果的bounds："+sfcGrid.getBounds().toBounds(crsSource));
        //SimpleFeatureIterator resultIterator = sfcGrid.features();
        //while (resultIterator.hasNext()) {
        //    SimpleFeature perHexagon = resultIterator.next();
        //    System.out.println("id: "+perHexagon.getAttribute("id"));
        //    System.out.println("wkt: "+((Geometry) perHexagon.getDefaultGeometry()).toText());
        //}
        pgDS.dispose();
    }

    public void test获取shp的原始列() throws Exception {
        String pathShp = "F:\\2.Data-GISTeam\\4.07月内部测试数据\\hotel1W.shp";
        String pathShp2 = "F:\\2.Data-GISTeam\\4.07月内部测试数据\\testDel.shp";
        SimpleFeatureSource sfs = ShpfileReader.getShpfile2(pathShp2, "utf-8");
        SimpleFeatureType schema = sfs.getSchema();
        System.out.println("属性列的数量：" + schema.getAttributeCount());
        for (AttributeDescriptor perAttrDes : schema.getAttributeDescriptors()) {
            System.out.println(perAttrDes.getLocalName());
        }
        SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
        typeBuilder.setName(schema.getName());
        typeBuilder.setSuperType((SimpleFeatureType) schema.getSuper());
        typeBuilder.addAll(schema.getAttributeDescriptors());
        typeBuilder.add("h3_index", String.class);
        SimpleFeatureType typeNew = typeBuilder.buildFeatureType();
        System.out.println("新结构的数量：" + typeNew.getAttributeCount());
        for (AttributeDescriptor perAttrDes : typeNew.getAttributeDescriptors()) {
            System.out.println(perAttrDes.getLocalName());
        }
        System.out.println("------------------");
        DataStore pgDS = connPostGIS.connPg();
        double resolution = 0.401234567;
        SimpleFeatureSource sfsSource = pgDS.getFeatureSource("hotel1W_1");
        SimpleFeatureType schemaPG = sfsSource.getSchema();
        //如果是pg的话，是无法获取到id哪一列的，很奇怪，而且更改了之后也没有该列了
        //不论是id列或者重新生成的fid列，都无法获取。
        System.out.println("pg列的数量：" + schemaPG.getAttributeCount());
        for (AttributeDescriptor perAttrDes : schemaPG.getAttributeDescriptors()) {
            System.out.println(perAttrDes.getLocalName());
        }
        System.out.println(schemaPG.getDescriptors().size());
    }

    public void test空间索引H3含义() throws IOException {
        h3Engine = H3Core.newInstance(H3CoreLoader.OperatingSystem.LINUX, "x64");
                //H3Core.newInstance();
        System.out.println(H3CoreLoader.OperatingSystem.LINUX);
        log.info("java.vendor:{}, os.name:{}",System.getProperty("java.vendor"), System.getProperty("os.name"));
        log.info("os.arch:{}",System.getProperty("os.arch"));
        /*
        w7jzgfndz7t
        * 计算需求：
        * (1)找出距离最近的n个点
        * (2)找出x范围内的所有点
        * */
        String pntSource = "109.49329,18.28188";
        String pnt2 = "109.49131,18.28188";
        String[] coords = pntSource.split(",");
        String[] coords2 = pnt2.split(",");
        System.out.println("x:" + coords[0] + " _===_ y:" + coords[1]);
        int res = 9;
        //首先，计算出原始点的h3索引值(address和long值是不一样的)
        String addressH3 = h3Engine.geoToH3Address(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), res);
        long longH3 = h3Engine.geoToH3(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), res);
        long longH3_2 = h3Engine.geoToH3(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), res + 1);
        String addressH3_2 = h3Engine.geoToH3Address(Double.parseDouble(coords[0]), Double.parseDouble(coords[1]), res + 1);
        String addressH3_3 = h3Engine.geoToH3Address(Double.parseDouble(coords2[0]), Double.parseDouble(coords2[1]), res + 1);
        System.out.println("address: " + addressH3);
        System.out.println("address2:" + addressH3_2);
        System.out.println("address3:" + addressH3_3);
        System.out.println("long: " + longH3);
        System.out.println("long2:" + longH3_2);
        System.out.println("res: " + h3Engine.h3GetResolution(addressH3_2));
        //如果是res级别，则应该搜索前res-1位上相同的字符
    }

    //此种方法不适合
    public void testUseH3_byRadius() {
        /*
         * 那范围搜索的方式应该变化，根据范围的大小转换为指定级别，
         * 然后创建获取该级别下的索引多边形，最后以该多边形为几何范围搜索范围内的点，做进一步分析
         * 但这样有个问题：有了搜索半径，就可以直接构造圆进行空间搜索了，为什么还要使用索引范围的方式呢？
         */
        String pntSource = "109.49329,18.28188";
        double radius = 5000;//搜索半径(单位:m),这时候就可以直接构造圆来搜索，为什么还要用索引呢？
        String[] srcPntCoords = pntSource.split(",");
        try {
            int resH3 = SPIUtils.getResH3(radius);
            System.out.println("H3级别是：" + resH3);
            h3Engine = H3Core.newInstance();
            //打开原始点集数据
            String pathShp = "F:\\2.Data-GISTeam\\4.07月内部测试数据\\hotel1W.shp";
            SimpleFeatureSource sfs = ShpfileReader.getShpfile2(pathShp, "utf-8");
            //String cln_H3Index="h3_index";
            ////获取原始数据的H3索引级别
            //Long h3LongSource = Long.getLong(sfs.getFeatures().features().next().getAttribute(cln_H3Index).toString());
            //int resH3 = h3Engine.h3GetResolution(h3LongSource);
            //计算输入坐标所在的H3边界
            long h3LongInput = h3Engine.geoToH3(Double.parseDouble(srcPntCoords[0]), Double.parseDouble(srcPntCoords[1]), resH3);
            List<GeoCoord> geoCoordList = h3Engine.h3ToGeoBoundary(h3LongInput);
            List<Coordinate> coordinates = geoCoordList.stream().
                    map(item -> {
                        System.out.println("x: " + item.lng + "_==_ y: " + item.lat);
                        return new Coordinate(item.lng, item.lat);
                    }).
                    collect(Collectors.toList());
            coordinates.add(new Coordinate(geoCoordList.get(0).lng, geoCoordList.get(0).lat));//必须要把起始点再添加一下才能构成面
            //以H3边界作为搜索几何，搜索落在该范围内的点
            Polygon filterBoundary = geometryFactory.createPolygon(coordinates.toArray(new Coordinate[geoCoordList.size()]));
            System.out.println("搜索框的面积：" + filterBoundary.getArea());
            String geoAttrName = sfs.getSchema().getGeometryDescriptor().getLocalName();
            System.out.println("几何属性列名称：" + geoAttrName);
            Filter geoFilter = filterFactory.intersects(filterFactory.property(geoAttrName),
                    filterFactory.literal(filterBoundary));
            SimpleFeatureCollection subCollection = sfs.getFeatures(geoFilter);
            //对搜索结果做进一步分析
            System.out.println("结果点数是：" + subCollection.size());
        } catch (Exception error) {
            System.out.print("执行失败：");
            System.out.println(error.getMessage());
        }
    }

    public void testUseH3_byNearest() {
        /*
         * 用于搜索指定个数的设施数量
         */
        String pntInput = "109.49329,18.28188";
        String[] srcCoordsInput = pntInput.split(",");
        try {
            h3Engine = H3Core.newInstance();
            //打开原始点集数据
            String pathShp = "F:\\2.Data-GISTeam\\4.07月内部测试数据\\hotel1W_h3_10.shp";
            String cln_H3Index = "h3_index";
            //获取原始数据的H3索引级别
            SimpleFeatureSource sfs = ShpfileReader.getShpfile2(pathShp, "utf-8");
            if (sfs.getSchema().indexOf(cln_H3Index) < 0) {
                throw new IllegalArgumentException("当前数据没有创建H3索引，无法计算！");
            }
            String spi = null;
            SimpleFeatureIterator itera = sfs.getFeatures().features();
            if (itera.hasNext()) {
                SimpleFeature feature = itera.next();
                System.out.println(feature.getAttribute("name"));
                Point pnt = (Point) feature.getDefaultGeometry();
                System.out.println(pnt.getX() + "," + pnt.getY());
                System.out.println(pntInput);
                spi = feature.getAttribute(cln_H3Index).toString();
            }
            itera.close();
            //Long h3LongSource = Long.decode(spi);//将一个字符串类型的long转换为long值
            System.out.println("SourceFeature address：" + spi);
            int h3Resolution = h3Engine.h3GetResolution(spi);
            log.warn("原始索引级别：{}",h3Resolution);
            //计算输入坐标的H3索引(顺序是：纬度，经度)
            String h3AddressInput = h3Engine.geoToH3Address(Double.parseDouble(srcCoordsInput[1]),
                    Double.parseDouble(srcCoordsInput[0]), h3Resolution);
            //查找所有相邻索引
            List<String> kRing = h3Engine.kRing(h3AddressInput, 1);
            System.out.println("kRing的数量："+kRing.size());
            System.out.println("输入坐标的索引地址："+h3AddressInput);//这一步的索引还是正确的
            //为什么获取的是相邻的索引，但是结果判断却都是不相邻呢？
            System.out.println("是否相邻："+h3Engine.h3IndexesAreNeighbors(h3AddressInput, kRing.get(0)));
            Set<String> likeStrs=new HashSet<String>();
            for (String perRing : kRing) {
                System.out.println(perRing);
                String sub = perRing.substring(0, h3Resolution);
                likeStrs.add(sub + "%");
            }
            log.info("待查询str个数：{}", likeStrs.size());
            for (String likeStr : likeStrs) {
                System.out.println(likeStr);
            }
            List<Filter> likeList=new ArrayList<>();
            for (String perLike : likeStrs) {
                //GeoTools中like语句的写法（注意后面通配符的设置，不然无法获取结果）
                likeList.add(filterFactory.like(filterFactory.property(cln_H3Index), perLike,
                        "%","_","\\"));
            }
            //Query query=new Query(cln_H3Index,likeList.get(0));
            SimpleFeatureCollection collectionSearch = sfs.getFeatures(filterFactory.or(likeList));
            if (collectionSearch.features().hasNext()) {
                System.out.println("具有结果:"+collectionSearch.size());
            }else {
                System.out.println("没有匹配到任何内容");
            }
            System.out.println("最终子集数："+collectionSearch.size());
        } catch (Exception error) {
            log.error("执行失败：", error);
        }
        System.out.println("执行结束");
    }
}
